package net.kleinhenz.questor.game.rules.simple;

import java.util.Iterator;
import java.util.List;

import net.kleinhenz.questor.exceptions.QuestorGameException;
import net.kleinhenz.questor.game.Dice;
import net.kleinhenz.questor.model.Battle;
import net.kleinhenz.questor.model.BattleClass;
import net.kleinhenz.questor.model.Fighter;
import net.kleinhenz.questor.model.Item;
import net.kleinhenz.questor.model.Skill;

/**
 * This implements a very lightweight battle rules system.
 * 
 * @author Michael Kleinhenz, m.kleinhenz@tarent.de
 */
public class SimpleBattle extends Battle implements BattleClass
{
	private static int[][] foeDamage = new int[][] {
			{6,0,0,0,0,1,2,3,4,5},			
			{7,0,0,0,1,2,3,4,5,6},
			{7,0,0,0,1,2,3,4,5,6},
			{8,0,0,1,2,3,4,5,6,7},
			{8,0,0,1,2,3,4,5,6,7},
			{9,0,1,2,3,4,5,6,7,8},
			{9,0,1,2,3,4,5,6,7,8},
			{10,1,2,3,4,5,6,7,8,9},
			{10,1,2,3,4,5,6,7,8,9},
			{11,2,3,4,5,6,7,8,9,10},
			{11,2,3,4,5,6,7,8,9,10},
			{12,3,4,5,6,7,8,9,10,11},
			{14,4,5,6,7,8,9,10,11,12},
			{14,4,5,6,7,8,9,10,11,12},
			{16,5,6,7,8,9,10,11,12,14},
			{16,5,6,7,8,9,10,11,12,14},
			{18,6,7,8,9,10,11,12,14,16},
			{18,6,7,8,9,10,11,12,14,16},
			{Integer.MAX_VALUE,7,8,9,10,11,12,14,16,18},
			{Integer.MAX_VALUE,7,8,9,10,11,12,14,16,18},
			{Integer.MAX_VALUE,8,9,10,11,12,14,16,18,Integer.MAX_VALUE},
			{Integer.MAX_VALUE,8,9,10,11,12,14,16,18,Integer.MAX_VALUE},
			{Integer.MAX_VALUE,9,10,11,12,14,16,18,Integer.MAX_VALUE,Integer.MAX_VALUE}
	};

	private static int[][] playerDamage = new int[][] {
			{0,Integer.MAX_VALUE,Integer.MAX_VALUE,8,8,7,6,5,4,3},
			{0,Integer.MAX_VALUE,8,7,7,6,6,5,4,3},
			{0,Integer.MAX_VALUE,8,7,7,6,6,5,4,3},
			{0,8,7,6,6,5,5,4,3,2},
			{0,8,7,6,6,5,5,4,3,2},
			{0,6,6,5,5,4,4,3,2,0},
			{0,6,6,5,5,4,4,3,2,0},
			{0,6,5,5,4,4,3,2,1,0},
			{0,6,5,5,4,4,3,2,1,0},
			{0,5,5,4,4,3,2,2,1,0},
			{0,5,5,4,4,3,2,2,1,0},
			{0,5,4,4,3,2,2,1,0,0},
			{0,5,4,3,3,2,2,1,0,0},
			{0,5,4,3,3,2,2,1,0,0},
			{0,4,3,3,2,2,2,1,0,0},
			{0,4,3,3,2,2,2,1,0,0},
			{0,4,3,3,2,2,1,0,0,0},
			{0,4,3,3,2,2,1,0,0,0},
			{0,4,3,2,2,2,1,0,0,0},
			{0,4,3,2,2,2,1,0,0,0},
			{0,3,3,2,2,2,1,0,0,0},
			{0,3,3,2,2,2,1,0,0,0},			
			{0,3,2,2,2,1,1,0,0,0},
	};

	public static void main(String[] args) throws QuestorGameException {

		Fighter player = new Fighter();
		player.setVariable(SimpleV1Rules.CHARACTER_ENDURANCE, 10);
		player.setVariable(SimpleV1Rules.CHARACTER_COMBATSKILL, 20);

		Fighter foe = new Fighter();
		foe.setVariable(SimpleV1Rules.CHARACTER_ENDURANCE, 25);
		foe.setVariable(SimpleV1Rules.CHARACTER_COMBATSKILL, 16);
		
		SimpleBattle battle = new SimpleBattle();
		System.out.println(battle.fight(player, foe));
	}

	@Override
	public Battle.Result fight(Fighter player, Fighter foe) throws QuestorGameException
	{		
		// check compliance
		checkCompliance(player);
		checkCompliance(foe);
			
		// calculate modified combat skills
		int playerMCS = calculateModifiedCombatSkill(player); 
		int foeMCS = calculateModifiedCombatSkill(foe);
		
		// calculate combat ratio
		int combatRatio = playerMCS - foeMCS;	
		System.out.println("Combat Skill Player: " + playerMCS + ", Combat Skill Foe: " + foeMCS);
		System.out.println("Combat Ratio: " + combatRatio);
		
		// scale to -11<=combatRatio<+11
		if (combatRatio>11)
			combatRatio = 11;
		else if (combatRatio<-11)
			combatRatio = -11;

		// FIXME: insert state callbacks to battle ui
		while (getEndurance(player)>0 && getEndurance(foe)>0)
			combatRound(combatRatio, player, foe);			

		// return result
		if (getEndurance(foe)<=0)
			return Battle.Result.PlayerWins;
		
		if (getEndurance(player)<=0)
			return Battle.Result.PlayerLoses;
	
		// can not happen
		return Battle.Result.Draw;
	}
	
	// battle round implementation follows
	
	private void combatRound(int combatRatio, Fighter player, Fighter foe)
	{
		int diceRoll = Dice.roll(10)-1;
		System.out.print("Dice Roll: " + diceRoll + " - ");
		int damagePlayer = playerDamage[combatRatio+11][diceRoll];
		int damageFoe = foeDamage[combatRatio+11][diceRoll];
		System.out.println("Damage Player: " + damagePlayer + ", Damage Foe: " + damageFoe);
		decEndurance(player, damagePlayer);
		decEndurance(foe, damageFoe);
		System.out.println("Player Endurance: " + getEndurance(player) + ", Endurance Foe: " + getEndurance(foe) + "\n");
	}

	// compliance checks follow
	
	private void checkCompliance(Fighter fighter) throws QuestorGameException
	{
		if (fighter.getVariableInteger(SimpleV1Rules.CHARACTER_COMBATSKILL)==null ||
				fighter.getVariableInteger(SimpleV1Rules.CHARACTER_ENDURANCE)==null)
			throw new QuestorGameException("Fighter does not comply to requirements of SimpleBattle. One or more needed variables are missing.");		
	}	
		
	// support methods follow
	
	private int getEndurance(Fighter fighter) {
		return fighter.getVariableInteger(SimpleV1Rules.CHARACTER_ENDURANCE);
	}

	private void decEndurance(Fighter fighter, int value) {
		fighter.setVariable(SimpleV1Rules.CHARACTER_ENDURANCE, getEndurance(fighter) - value);
	}
	
	private boolean hasWeapon(Fighter fighter, String weaponName) {
		Iterator<Item> iter = fighter.getItems(SimpleV1Rules.ITEMTYPE_WEAPON).iterator();
		while (iter.hasNext())
			if (iter.next().getName().equals(weaponName))
				return true;
		
		return false;		
	}
	
	private int calculateModifiedCombatSkill(Fighter fighter) {

		// get base value
		int value = fighter.getVariableInteger(SimpleV1Rules.CHARACTER_COMBATSKILL);
		
		// filter weaponskills
		List<Skill> skills = fighter.getSkills(SimpleV1Rules.SKILLTYPE_WEAPONSKILL);

		// for each weaponskill, check if the fighter has the weapon
		Iterator<Skill> iter = skills.iterator();
		while (iter.hasNext()) {
			Skill thisSkill = iter.next();
			if (hasWeapon(fighter, thisSkill.getVariableString(SimpleV1Rules.SKILLWEAPONSKILL_KEY_WEAPON)))
				// if he has the weapon, modify the combat skill value
				value += thisSkill.getVariableInteger(SimpleV1Rules.SKILLWEAPONSKILL_KEY_MODIFIER);
		}
		
		return value;
	}
}
